class JavaPrinter extends TreeParser;

options {
	importVocab = Java;
}

{

    public LinkedList getLines() {
        return _lines;
    }

    public void setCinfo(ClassData info) {
        _currentCInfo = info;
    }
    
    ClassData _currentCInfo;
    Stack cinfoStack = new Stack();

    private LinkedList _lines = new LinkedList();

    private StringBuffer _currentID;
    private StringBuffer _currentModifiers;
    private LinkedList _idList = new LinkedList();
    private int _ntypeArray;
    private String _typeName;
    private String _varInitializer = null;
    private StringBuffer _currentEXPR;
    private String _currentConst;
    private LinkedList _exprList = new LinkedList();
    private Stack _exprStack = new Stack();
    private StringBuffer _arrayIndexing;
    private StringBuffer _methodHead;
    private StringBuffer _currentPar;
    private LinkedList _parList = new LinkedList();
    private String _methodName;
    private StringBuffer _initializer;
    private StringBuffer _currentArrayDecl;
    
    private int _ident = 0;
    private static final int _IDENT_INC = 4;
}

compilationUnit
	:
            (packageDefinition)?
            (importDefinition)*
            (typeDefinition)*
            |
            objBlock  // essa regra e para classes anonimas
	;

packageDefinition
        :	#( n1:PACKAGE_DEF identifier  {
                     Line l = new Line("package " + _currentID.toString() +";", 
                         n1, null, 2, _ident, _lines.size()); 
                     _lines.add(l); } )
	;

importDefinition
	:	#( n1:IMPORT identifierStar { 
	            Line l = new Line("import " + _currentID.toString() + ";", 
		        n1, null, 1, _ident, _lines.size()); 
		    _lines.add(l); } )
	;

typeDefinition  { StringBuffer line = new StringBuffer(); boolean isInner = false; }

	:	#( n1:CLASS_DEF modifiers { line.append(_currentModifiers.toString() + " class "); }
                    n2:IDENT  {
                        line.append(n2.getText() + " ");

                        // Verifico se e uma classe declarada dentro de outra
                        
                        ClassData ci = _currentCInfo.getInnerClass(_currentCInfo.getName() + "." +
                            n2.getText());
                        if(ci != null) {
                            // Neste caso, e uma classe interna declarada fora de um 
                            // metodo. Entao copio o codigo ja gerado.
                            // Quando for classe anonima, um uma classe local, ai
                            // o codeGen do FlowGraph da conta do recado.
                            
                            LinkedList code = ci.getCode();
                            for(int i = 0;i < code.size();i++) {
                                Line l = (Line) code.get(i);
                                // Eu duplico a linha, para o numero da linha 
                                // ficar correto
                                Line newl = l.duplicate();
                                newl.setIdent(l.getIdent() + _ident);
                                newl.setNumber(_lines.size());
                                _lines.add(newl);
                            }
                            return;
                            /*
                            cinfoStack.push(_currentCInfo);
                            _currentCInfo = ci;
                            isInner = true;*/
                        }				    
                    }

                    extendsClause {
                        if(_idList.size() > 0) {
                          line.append("extends ");
                          for(int i = 0;i < _idList.size() - 1;i++) {
                              String s = (String) _idList.get(i);
                              line.append(s + ",");
                          }
                          String s = (String) _idList.get(_idList.size() - 1);
                          line.append(s + " ");
                        }
                    }

                    implementsClause {
                        if(_idList.size() > 0) {
                          line.append("implements ");
                          for(int i = 0;i < _idList.size() - 1;i++) {
                              String s = (String) _idList.get(i);
                              line.append(s + ",");
                          }
                          String s = (String) _idList.get(_idList.size() - 1);
                          line.append(s + " ");
                        }

                        line.append("{");
                        Line l = new Line(line.toString(), n1, null, 2, _ident, _lines.size());
                        _lines.add(l);
                    }

                    { _ident += _IDENT_INC; }
                    objBlock  {
                        _ident -= _IDENT_INC;
                        Line l2 = new Line("}", null, null, 1, _ident, _lines.size()); 
                        _lines.add(l2); 
/*
                        if(isInner) {
                            _currentCInfo = (ClassData) cinfoStack.pop();
                        } */
                        
                    } )

	|	#(n3:INTERFACE_DEF modifiers { line.append(_currentModifiers.toString() + " interface "); }
                       n4:IDENT  { line.append(n4.getText() + " "); }
                       
                       extendsClause {
                           if(_idList.size() > 0) {
                               line.append("extends ");
                               for(int i = 0;i < _idList.size() - 1;i++) {
                                   String s = (String) _idList.get(i);
                                   line.append(s + ",");
                               }
                               String s = (String) _idList.get(_idList.size() - 1);
                              line.append(s + " ");
                            }
                       
                           line.append("{");
                           Line l = new Line(line.toString(), n3, null, 2, _ident, _lines.size());
                           _lines.add(l);
                       }
    
                       { _ident += _IDENT_INC; }
                       interfaceBlock {
                           _ident -= _IDENT_INC;   
                           Line l2 = new Line("}", null, null, 1, _ident, _lines.size()); 
                           _lines.add(l2); } )
	;

typeSpec        { _ntypeArray = 0; }
	:	#(TYPE typeSpecArray)
	;

typeSpecArray   
	:	#( ARRAY_DECLARATOR { _ntypeArray++; } typeSpecArray )
	|	type
	;

type:	identifier 		{ _typeName = _currentID.toString(); }
	|	n1:builtInType	{ _typeName = n1.getText(); }
	;

builtInType
    :   "void"
    |   "boolean"
    |   "byte"
    |   "char"
    |   "short"
    |   "int"
    |   "float"
    |   "long"
    |   "double"
    ;

modifiers   { _currentModifiers = new StringBuffer(); }
	:	#( MODIFIERS (n1:modifier { _currentModifiers.append(n1.getText() + " "); } )* )    
	;

modifier
    :   "private"
    |   "public"
    |   "protected"
    |   "static"
    |   "transient"
    |   "final"
    |   "abstract"
    |   "native"
    |   "threadsafe"
    |   "synchronized"
    |   "const"
    |   "volatile"
	|	"strictfp"
    ;

extendsClause 		{ _idList.clear(); }
	:	#(EXTENDS_CLAUSE (identifier { _idList.add(_currentID.toString()); } )* )
	;

implementsClause	{ _idList.clear(); }
	:	#(IMPLEMENTS_CLAUSE (identifier { _idList.add(_currentID.toString()); } )* )
	;


interfaceBlock
	:	#(	OBJBLOCK
			(	methodDecl

			|	variableDef

			)*
		)
	;
	
objBlock
	:	#(	OBJBLOCK
			(	ctorDef

			|	methodDef
			
			|	variableDef 
			
			|	typeDefinition
			
//			|	#(STATIC_INIT slist)   // ????????
			
//			|	#(INSTANCE_INIT slist)  // ?????????
			)*
		)
	;

ctorDef
	:	#(n1:CTOR_DEF { StringBuffer line = new StringBuffer(); }
	              modifiers { line.append(_currentModifiers.toString() + " "); } 
		      methodHead { 
		           line.append(_methodHead.toString()); 
		           line.append(" {"); 
		           Line l = new Line(line.toString(), n1, null, 1, _ident, _lines.size()); 
			   _lines.add(l);
	              }

		      ctorSList
		      
		      {
		          Iterator ite = _currentCInfo.constructorsIterator();
			  while(ite.hasNext()) {
			      STMethodEntry me = (STMethodEntry) ite.next();
			      AST node = me.getNode();
			      if(node.equalsTree(n1)) {
  		                  FlowGraph fg = me.getFlowGraph();
 			          fg.generateCode(_lines, _ident + _IDENT_INC);
				  break;
			      }
			  }
		      }

		      { l = new Line("}", null, null, 2, _ident, _lines.size());
		        _lines.add(l);} )
	;

methodDecl
	:	#(n1: METHOD_DEF { StringBuffer line = new StringBuffer(); }
	              modifiers { line.append(_currentModifiers.toString() + " "); }
		      typeSpec {
		          line.append(_typeName + " ");
			  for(int i = 0;i < _ntypeArray;i++) {
			      line.append("[]");
			  }
			  line.append(" ");
		      }
		      methodHead {
		           line.append(_methodHead.toString());
		           line.append(";");
		           Line l = new Line(line.toString(), n1, null, 1, _ident, _lines.size()); 
			   _lines.add(l);
	              })
	;

methodDef
	:	#(n1:METHOD_DEF { StringBuffer line = new StringBuffer(); }
	              modifiers { line.append(_currentModifiers.toString() + " "); }
		      typeSpec {
		          line.append(_typeName + " ");
			  for(int i = 0;i < _ntypeArray;i++) {
			      line.append("[]");
			  }
			  line.append(" ");
		      }		      
		      methodHead { 
		           line.append(_methodHead.toString()); 
		           line.append(" {"); 
		           Line l = new Line(line.toString(), n1, null, 1, _ident, _lines.size()); 
			   _lines.add(l);
	              }

		      (slist)?
		      
		      {
			  LinkedList ml = (LinkedList) _currentCInfo.getMethod(_methodName);
                          if(ml != null) {
                              for(int i = 0;i < ml.size();i++) {
                                  STMethodEntry me = (STMethodEntry) ml.get(i);
                                  if( n1.equalsTree(me.getNode()) ) {
                                      FlowGraph fg = me.getFlowGraph();
                                      fg.generateCode(_lines, _ident + _IDENT_INC);
                                      l = new Line("}", null, null, 2, _ident, _lines.size()); 
                                      _lines.add(l);
                                      break;
                                  }
                              }
                          }
                          else {
                              l = new Line("Not processed", null, null, 2, 
                                  _ident, _lines.size());
                              l = new Line("}", null, null, 2, _ident, _lines.size());
                          }
		      } )
	;


variableDef    	{ StringBuffer line = new StringBuffer(); }
	:	#(n1:VARIABLE_DEF modifiers { line.append(_currentModifiers.toString()); }
                      typeSpec  {
                          line.append(_typeName + " ");
                          for(int i = 0;i < _ntypeArray;i++) {
                              line.append("[]");
                          }
                          line.append(" ");
                      }

                      variableDeclarator {
                          line.append(_currentID.toString() + " ");
                      }			

                      varInitializer {
                          if(_varInitializer != null) {				          
                              line.append(_varInitializer);
                              _varInitializer = null;
                          }
                      } 

                      { line.append(";"); 
                        Line l = new Line(line.toString(), n1, null, 1, _ident, _lines.size()); 
                        _lines.add(l); } )
	;

parameterDef    { _currentPar = new StringBuffer(); }
	:	#(PARAMETER_DEF 
	            modifiers { _currentPar.append(_currentModifiers.toString()); }
	            typeSpec {
		        _currentPar.append(_typeName + " ");
		        for(int i = 0;i < _ntypeArray;i++) {
			    _currentPar.append("[]");
			}
			_currentPar.append(" ");
		    }
		    n1:IDENT { _currentPar.append(n1.getText()); } )
	;

variableDeclarator 				{ _currentID = new StringBuffer(); }
	:	n1:IDENT 			{ _currentID.append(n1.getText()); }
	|	LBRACK variableDeclarator  	{ System.out.println("UMA SITUACAO NAO PROCESSADA : LBRACK variableDeclarator"); }
	;

varInitializer  { _initializer = new StringBuffer(); }
	:	#(ASSIGN initializer) { _varInitializer = new String("= " + _initializer.toString()); }
	|
	;

initializer    { _currentEXPR = new StringBuffer(); }
	:	expression { _initializer.append(_currentEXPR.toString()); }
	|	arrayInitializer
	;

arrayInitializer
	:	#(ARRAY_INIT { _initializer.append("{"); } 
	           initializer ({_initializer.append(","); } initializer)* { _initializer.append("}"); }
		  )
	;

methodHead   { _methodHead = new StringBuffer(); _parList.clear(); _idList.clear(); }
	:	n1:IDENT { _methodHead.append(n1.getText() + "("); _methodName = n1.getText(); } 
	        #( PARAMETERS (parameterDef {_parList.add(_currentPar.toString());} )* ) {
		    if(_parList.size() > 0) {
                        for(int i = 0;i < _parList.size() - 1;i++) {
			    String s = (String) _parList.get(i);
                            _methodHead.append(s + ", ");
			}
			String s = (String) _parList.get(_parList.size() - 1);
			_methodHead.append(s);
		    }
		    _methodHead.append(")");
		
		} 
		
		(throwsClause)? 
		{
		    if(_idList.size() > 0) {
		        _methodHead.append(" throws ");
		        for(int i = 0;i < _idList.size() - 1;i++) {
			    String s = (String) _idList.get(i);
			    _methodHead.append(s + ", ");
			}			
			_methodHead.append((String) _idList.get(_idList.size() - 1));		    		    
                        _idList.clear();
		    }
		}
	;

throwsClause     { _idList.clear(); }
	:	#( "throws" (identifier {_idList.add(_currentID.toString());} )* )
	;

identifier    { _currentID = new StringBuffer(); } 
	:	x:IDENT  		    { _currentID.append(x.getText()); }
	|	#( DOT identifier y:IDENT ) { _currentID.append("." + y.getText()); }
	;

identifierStar	{ _currentID = new StringBuffer(); } 
	:	x:IDENT                       	 { _currentID.append(x.getText()); }
	|	#( DOT identifier (y:STAR { _currentID.append(".*"); } | y2:IDENT { _currentID.append("." + y2.getText()); } ) )
	;


ctorSList
	:	SLIST
	;

slist
	:	SLIST
	;

elist      { _exprStack.push(_exprList); _exprList = new LinkedList(); }
	:	#( ELIST (expression { _exprList.add(_currentEXPR.toString()); } )* )
	;

expression
	:	#(EXPR expr)
	;

expr: 
	        #(QUESTION expr { String _exp1 = _currentEXPR.toString(); }  
		           expr { String _exp2 = _currentEXPR.toString(); } 
			   expr { String _exp3 = _currentEXPR.toString(); 
			       _currentEXPR = new StringBuffer("(" + _exp1 + ")?" + _exp2 + ":" + _exp3); 
			   } )

	|	#(ASSIGN expr { String _exp1 = _currentEXPR.toString(); } 
	                 expr { String _exp2 = _currentEXPR.toString();
                               _currentEXPR = new StringBuffer(_exp1 + " = " + _exp2); 
			 } )

	|	#(PLUS_ASSIGN expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer(_exp1 + " += " + _exp2);
			      } )

	|	#(MINUS_ASSIGN expr { String _exp1 = _currentEXPR.toString(); }
			       expr { String _exp2 = _currentEXPR.toString();
			          _currentEXPR = new StringBuffer(_exp1 + " -= " + _exp2);
		 	       } )

	|	#(STAR_ASSIGN expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer(_exp1 + " *= " + _exp2);
			      } )

	|	#(DIV_ASSIGN expr { String _exp1 = _currentEXPR.toString(); }
			     expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer(_exp1 + " /= " + _exp2);
			     } )

	|	#(MOD_ASSIGN  expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString();
			          _currentEXPR = new StringBuffer(_exp1 + " %= " + _exp2);
			      } )

	|	#(SR_ASSIGN expr { String _exp1 = _currentEXPR.toString(); }
			    expr { String _exp2 = _currentEXPR.toString();
			          _currentEXPR = new StringBuffer(_exp1 + " >>= " + _exp2);
			    } )

	|	#(BSR_ASSIGN  expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer(_exp1 + " >>>= " + _exp2);
			      } )

	|	#(SL_ASSIGN   expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer(_exp1 + " <<= " + _exp2);
			      } )

	|	#(BAND_ASSIGN expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer(_exp1 + " &= " + _exp2);
			      } )

	|	#(BXOR_ASSIGN expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer(_exp1 + " ^= " + _exp2);
			      } )

	|	#(BOR_ASSIGN  expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer(_exp1 + " |= " + _exp2);
			      } )

	|	#(LOR expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " || " + _exp2 + ")");
			      } )

	|	#(LAND expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " && " + _exp2 + ")");
			      } )

	|	#(BOR expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " | " + _exp2 + ")");
			      } )

	|	#(BXOR expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " ^ " + _exp2 + ")");
			      } )

	|	#(BAND expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " & " + _exp2 + ")");
			      } )

	|	#(NOT_EQUAL expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " != " + _exp2 + ")");
			      } )

	|	#(EQUAL expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " == " + _exp2 + ")");
			      } )

	|	#(LT expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " < " + _exp2 + ")");
			      } )

	|	#(GT expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " > " + _exp2 + ")");
			      } )

	|	#(LE expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " <= " + _exp2 + ")");
			      } )

	|	#(GE expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " >= " + _exp2 + ")");
			      } )

	|	#(SL expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " << " + _exp2 + ")");
			      } )

	|	#(SR expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " >> " + _exp2 + ")");
			      } )

	|	#(BSR expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " >>> " + _exp2 + ")");
			      } )

	|	#(PLUS expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " + " + _exp2 + ")");
			      } )

	|	#(MINUS expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " - " + _exp2 + ")");
			      } )

	|	#(DIV expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " / " + _exp2 + ")");
			      } )

	|	#(MOD expr { String _exp1 = _currentEXPR.toString(); }
			      expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " % " + _exp2 + ")");
			      } )

	|	#(STAR expr { String _exp1 = _currentEXPR.toString(); }
		       expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " * " + _exp2 + ")");
			      } )
	
	|	#(INC expr { String _exp1 = _currentEXPR.toString(); 
	                     _currentEXPR = new StringBuffer("++" + _exp1); 
			   })

	|	#(DEC expr { String _exp1 = _currentEXPR.toString(); 
	                     _currentEXPR = new StringBuffer("--" + _exp1); } )

	|	#(POST_INC expr { String _exp1 = _currentEXPR.toString(); 
	                          _currentEXPR = new StringBuffer(_exp1 + "++"); } )

	|	#(POST_DEC expr { String _exp1 = _currentEXPR.toString(); 
	                          _currentEXPR = new StringBuffer(_exp1 + "--"); } )

	|	#(BNOT expr { String _exp1 = _currentEXPR.toString(); 
	                      _currentEXPR = new StringBuffer("(~" + _exp1 + ")"); } )

	|	#(LNOT expr { String _exp1 = _currentEXPR.toString(); 
	                      _currentEXPR = new StringBuffer("(!" + _exp1 + ")"); } )

	|	#("instanceof" expr { String _exp1 = _currentEXPR.toString(); }
			       expr { String _exp2 = _currentEXPR.toString(); 
			          _currentEXPR = new StringBuffer("(" + _exp1 + " instanceof " + _exp2 + ")");
			       } )

	|	#(UNARY_MINUS expr { String _exp1 = _currentEXPR.toString(); 
	                             _currentEXPR = new StringBuffer("-" + _exp1); } )

	|	#(UNARY_PLUS expr { String _exp1 = _currentEXPR.toString(); 
	                            _currentEXPR = new StringBuffer("+" + _exp1); } )

	|	primaryExpression

	;
primaryExpression
    :   n1:IDENT { _currentEXPR = new StringBuffer(n1.getText()); }

    |   #(	DOT   { String dot; }
			(	expr  { dot = _currentEXPR.toString();  }
				(	n2:IDENT { _currentEXPR = new StringBuffer(dot + "." + n2.getText()); }

				|	arrayIndex
				|	"this"
				
				|	"class" { _currentEXPR = new StringBuffer(dot + ".class"); }

				|	#( "new" IDENT elist )
				|   "super"
				)
				
			|	#(ARRAY_DECLARATOR typeSpecArray)

			|	builtInType ("class")?
			)
		)

	|	arrayIndex { _currentEXPR = new StringBuffer(_arrayIndexing.toString()); }

	|	#(METHOD_CALL { StringBuffer ret = new StringBuffer(); }
	             primaryExpression { ret.append(_currentEXPR.toString() + "("); }
		     elist {
		         if(_exprList.size() > 0) {
		             for(int i = 0;i < _exprList.size() - 1;i++) {
			         ret.append((String) _exprList.get(i) + ",");
			     }
			     ret.append((String) _exprList.get(_exprList.size() - 1));
			 }
			 ret.append(")");
			 _currentEXPR = new StringBuffer(ret.toString());
			 
			 _exprList = (LinkedList) _exprStack.pop();
		     } )

	|	#(TYPECAST { StringBuffer ret = new StringBuffer("(("); }
	             typeSpec {
		         ret.append(_typeName + " ");
		         for(int i = 0;i < _ntypeArray;i++) {
		             ret.append("[]");
		         }
			 ret.append(")");
		     }
		     expr { ret.append(_currentEXPR.toString() + ")"); 
		         _currentEXPR = new StringBuffer(ret.toString()); 
	             } )

	|   newExpression

	|   constant 	{ _currentEXPR = new StringBuffer(_currentConst); }    
        |   "super" 	{ _currentEXPR = new StringBuffer("super"); }
        |   "true" 	{ _currentEXPR = new StringBuffer("true"); }
        |   "false" 	{ _currentEXPR = new StringBuffer("false"); }
        |   "this" 	{ _currentEXPR = new StringBuffer("this"); }
        |   "null" 	{ _currentEXPR = new StringBuffer("null"); }

        |	// type name used with instanceof
		typeSpec { _currentEXPR = new StringBuffer(_typeName); }

	;

ctorCall
	:	#( CTOR_CALL elist )
	|	#( SUPER_CTOR_CALL
			(	elist
			|	primaryExpression elist
			)
		 )
	;

arrayIndex
	:	#(INDEX_OP { _arrayIndexing = new StringBuffer(); }
	              primaryExpression { _arrayIndexing.append(_currentEXPR.toString() + "["); } 
		      expression { _arrayIndexing.append(_currentEXPR.toString() + "]"); } )
	;

constant
    :   n1:NUM_INT 		{ _currentConst = n1.getText(); }
    |   n2:CHAR_LITERAL	        { _currentConst = n2.getText(); }
    |   n3:STRING_LITERAL	{ _currentConst = n3.getText(); }
    |   n4:NUM_FLOAT		{ _currentConst = n4.getText(); }
    |   n5:NUM_DOUBLE		{ _currentConst = n5.getText(); }
    |   n6:NUM_LONG		{ _currentConst = n6.getText(); }
    ;

newExpression   { StringBuffer ret = new StringBuffer("new "); }
	:	#(	"new" type { ret.append(_typeName); }
	
			(	{ _currentArrayDecl = new StringBuffer(); } 
			        newArrayDeclarator {
				    ret.append(_currentArrayDecl.toString());
			        }

				(arrayInitializer {
				    ret.append("= " + _initializer.toString());
				})?

				{ _currentEXPR = new StringBuffer(ret.toString()); }
			
			|	elist  {         // (objBlock)?
			            ret.append("(");
                                    if(_exprList.size() > 0) {
 		                       for(int i = 0;i < _exprList.size() - 1;i++) {
			                   ret.append((String) _exprList.get(i) + ",");
			                }
			                ret.append((String) _exprList.get(_exprList.size() - 1));
			            }
			            ret.append(")");
			            _currentEXPR = new StringBuffer(ret.toString());

                                    _exprList = (LinkedList) _exprStack.pop();
			        }
			)
		)
	;

newArrayDeclarator  { boolean hasEXPR = false; }
	:	#( ARRAY_DECLARATOR 

		       (newArrayDeclarator)? 

		       (expression {		       
			    _currentArrayDecl.append("[" + _currentEXPR.toString() + "]");
                            hasEXPR = true;
		        } )? 
		       
		        {
		            if(!hasEXPR) {
			       _currentArrayDecl.append("[]");			   
			    }
		        }
		 )
	;
